#include <stdlib.h>
#include <string.h>

#include "fs.h"
#include "metafs.h"
#include "isofs.h"
#include "dirfs.h"
#include "../log.h"

enum step {
    DRV,
    PTH,
    FLE,
    EXT
};

SplittedPath splitPath(const char *filename)
{
    s32 i, j = 0;
    s32 len = strlen(filename);
    s32 step = DRV;
    s32 firststep;
    s32 ndirs = -1;
    s32 startndirs;
    SplittedPath path;
    u32 szdrv, szpath, szfile, szext;
    szdrv = szpath = szfile = szext = 0;

    for (i = 0; i < len; i++)
        if (filename[i] == PTHSPRT)
            ndirs++;

    if (ndirs == -1)
        step = FLE;
#ifdef UNIX
    else if (filename[0] != PTHSPRT) {
#else
    else if (filename[2] != PTHSPRT) {
#endif
        step = PTH;
        ndirs++;
    }

    startndirs = ndirs;
    firststep = step;

    for (i = 0; i < len; i++)
    {
        switch (step)
        {
        case DRV:
            if (filename[i] != PTHSPRT) {
                szdrv++;
                continue;
            }
            szdrv++;
            step = PTH;
            break;

        case PTH:
            if (filename[i] == PTHSPRT)
                ndirs--;
            if (ndirs == 0)
            {
                szpath++;
                step = FLE;
                continue;
            }
            szpath++;
            break;

        case FLE:
            if (filename[i] == '.')
            {
                szext = 1;
                step = EXT;
                continue;
            }
            szfile++;
            break;

        default:
            szext++;
            break;
        }
    }
    
    path.drive = malloc(szdrv + 1);
    path.drive[0] = '\0';
    path.path = malloc(szpath + 1);
    path.path[0] = '\0';
    path.file = malloc(szfile + 1);
    path.file[0] = '\0';
    path.ext = malloc(szext + 1);
    path.ext[0] = '\0';

    ndirs = startndirs;
    step = firststep;

    for (i = 0; i < len; i++)
    {
        switch (step)
        {
        case DRV:
            if (filename[i] != PTHSPRT) {
                path.drive[j++] = filename[i];
                continue;
            }
            path.drive[j] = filename[i];
            path.drive[j + 1] = '\0';
            step = PTH;
            j = 0;
            break;

        case PTH:
            if (filename[i] == PTHSPRT)
                ndirs--;
            if (ndirs == 0) {
                path.path[j] = PTHSPRT;
                path.path[j + 1] = '\0';
                step = FLE;
                j = 0;
                continue;
            }
            path.path[j] = filename[i];
            j++;
            break;

        case FLE:
            if (filename[i] == '.') {
                path.file[j] = '\0';
                step = EXT;
                j = 0;
                path.ext[j++] = '.';
                continue;
            }
            path.file[j] = filename[i];
            j++;
            break;

        default:
            path.ext[j] = filename[i];
            j++;
            break;
        }
    }
    if (step == FLE) {
        path.file[j] = '\0';
        path.ext[0] = '\0';
    }
    else if (step == EXT)
        path.ext[j] = '\0';

    return path;
}

void freeSplittedPath(SplittedPath *path)
{
    free(path->drive);
    free(path->path);
    free(path->file);
    free(path->ext);
}

void fileinfo_init(FileInfo *fi)
{
    fi->size = 0;
    fi->access = 0;
    fi->type = FILETYPE_NORMAL;
    fi->isOnSectorSystem = 0;
}

char *fs_getRealPath(System *fs, const char *inpath)
{
    if (fs->type == DIRFS)
        return dfs_getRealPath(fs, inpath);
    return NULL;
}

FileInfo fs_getFileInfo(System *fs, const char *filename)
{
    FileInfo empty;
    fileinfo_init(&empty);

    switch (fs->type)
    {
    case ISOFS:
        return iso_getFileInfo(fs, filename);

    case DIRFS:
        return dfs_getFileInfo(fs, filename);

    default:
        _log(ERR, FS, "Problem with %s: unrecognized fs type %d !", __FUNCTION__, fs->type);
        return empty;
    }
}

s32 fs_ownsHandle(System *fs, u32 handle)
{
    switch (fs->type)
    {
    case ISOFS:
        return iso_ownsHandle(fs, handle);

    case DIRFS:
        return dfs_ownsHandle(fs, handle);

    default:
        _log(ERR, FS, "Problem with %s: unrecognized fs type %d !", __FUNCTION__, fs->type);
        return 0;
    }
}

s32 fs_readFile(System *fs, u32 handle, char *pointer, u32 size)
{
    switch (fs->type)
    {
    case ISOFS:
        return iso_readFile(fs, handle, pointer, size);

    case DIRFS:
        return dfs_readFile(fs, handle, pointer, size);

    default:
        _log(ERR, FS, "Problem with %s: unrecognized fs type %d !", __FUNCTION__, fs->type);
        return 0;
    }
}

s32 fs_readFileToAddr(System *fs, u32 handle, u32 addr, u32 size)
{
    switch (fs->type)
    {
    case ISOFS:
        return iso_readFileToAddr(fs, handle, addr, size);

    case DIRFS:
        return dfs_readFileToAddr(fs, handle, addr, size);

    default:
        _log(ERR, FS, "Problem with %s: unrecognized fs type %d !", __FUNCTION__, fs->type);
        return 0;
    }
}

s32 fs_openFile(System *fs, const char *filename, FileAccess access)
{
    switch (fs->type)
    {
    case ISOFS:
        return iso_openFile(fs, filename, access);

    case DIRFS:
        return dfs_openFile(fs, filename, access);

    default:
        _log(ERR, FS, "Problem with %s: unrecognized fs type %d !", __FUNCTION__, fs->type);
        return 0;
    }
}

void fs_closeFile(System *fs, u32 handle)
{
    switch (fs->type)
    {
    case ISOFS:
        iso_closeFile(fs, handle);
        break;

    case DIRFS:
        dfs_closeFile(fs, handle);
        break;

    default:
        _log(ERR, FS, "Problem with %s: unrecognized fs type %d !", __FUNCTION__, fs->type);
        break;
    }
}

s32 fs_seekFile(System *fs, u32 handle, s32 position, FileMove type)
{
    switch (fs->type)
    {
    case ISOFS:
        return iso_seekFile(fs, handle, position, type);

    case DIRFS:
        return dfs_seekFile(fs, handle, position, type);

    default:
        _log(ERR, FS, "Problem with %s: unrecognized fs type %d !", __FUNCTION__, fs->type);
        return 0;
    }
}

s32 fs_writeFile(System *fs, u32 handle, u8 *pointer, u32 size)
{
    switch (fs->type)
    {
    case DIRFS:
        return dfs_writeFile(fs, handle, pointer, size);

    default:
        _log(ERR, FS, "Problem with %s: unrecognized fs type %d !", __FUNCTION__, fs->type);
        return 0;
    }
}

s32 fs_writeFileFromAddr(System *fs, u32 handle, u32 addr, u32 size)
{
    switch (fs->type)
    {
    case DIRFS:
        return dfs_writeFileFromAddr(fs, handle, addr, size);

    default:
        _log(ERR, FS, "Problem with %s: unrecognized fs type %d !", __FUNCTION__, fs->type);
        return 0;
    }
}

s8 fs_mkdir(System *fs, const char *dir, s32 mode)
{
    switch (fs->type)
    {
    case DIRFS:
        return dfs_mkdir(fs, dir, mode);

    default:
        _log(ERR, FS, "Problem with %s: unrecognized fs type %d !", __FUNCTION__, fs->type);
        return -1;
    }
}

u32 fs_getDirListing(System *fs, const char *path, FileInfo **listing)
{
    switch (fs->type)
    {
    case ISOFS:
        return iso_getDirListing(fs, path, listing);

    case DIRFS:
        return dfs_getDirListing(fs, path, listing);

    default:
        _log(ERR, FS, "Problem with %s: unrecognized fs type %d !", __FUNCTION__, fs->type);
        return 0;
    }
}

void fs_freeDirListing(System *fs, FileInfo *listing)
{
    switch (fs->type)
    {
    case ISOFS:
        iso_freeDirListing(listing);
        break;

    case DIRFS:
        dfs_freeDirListing(listing);
        break;

    default:
        _log(ERR, FS, "Problem with %s: unrecognized fs type %d !", __FUNCTION__, fs->type);
        break;
    }
}

void fs_free(System *fs)
{
    switch (fs->type)
    {
    case ISOFS:
        iso_free(fs);
        break;

    case DIRFS:
        dfs_free(fs);
        break;
    }
}

